#include "gui/Viewport.h"

#include "util/MathUtils.h"
#include "gui/GUIEngine.h"
#include "gui/layers/MapLayer.h"
#include "gui/layers/InfoLayer.h"

namespace gui {

Viewport::Viewport(GUIMap* guiMap, GUIRoute* guiRoute, int width, int height, int scrollSpeed = 10) {
	this->guiMap = guiMap;
	this->guiRoute = guiRoute;
	this->height = height;
	this->width = width;
	this->setScrollSpeed(scrollSpeed);

	//TODO: Crear los layers. Por ahora no se puede porque no tengo
	// 	todos los parametros que necesitan para crearse
	this->background = new MapLayer(guiMap);
	//this->foreground = new InfoLayer();


}

Viewport::~Viewport() {
	delete this->guiMap;
	delete this->background;
	delete this->foreground;
}

void Viewport::setScrollSpeed(int scrollSpeed) {
	this->scrollSpeed = scrollSpeed;
}

void Viewport::moveViewport(ViewportMove horizontalMovement, ViewportMove verticalMovement) {
	int xMove = 0;
	int yMove = 0;
	switch (horizontalMovement) {
		case Ini: this->portionToDraw.x = 0; break;
		case MoveLeft: xMove += this->scrollSpeed; break;
		case MoveRight: xMove -= this->scrollSpeed; break;
		default: break;
	}
	switch (verticalMovement) {
		case Ini: this->portionToDraw.y = 0; break;
		case MoveUp: yMove += this->scrollSpeed; break;
		case MoveDown: yMove -= this->scrollSpeed; break;
		default: break;
	}
	this->portionToDraw.h = this->height;
	this->portionToDraw.w = this->width;
	//Una de dos, se usa esto:
	this->adaptScrollToLimits(portionToDraw, &xMove, &yMove);
	this->portionToDraw.x += xMove;
	this->portionToDraw.y += yMove;
	// O se usa esto:
//	if (this->checkForLimits(portionToDraw, xMove, yMove)) {
//		this->portionToDraw.x += xMove;
//		this->portionToDraw.y += yMove;
//	}
}

// Este metodo es equivalente a checkForLimits
void Viewport::adaptScrollToLimits(Portion portion, int* xMoveOrigial, int* yMoveOriginal){
	// inicio parche (se invierten los valores)
	int xMove = -(*xMoveOrigial);
	int yMove = -(*yMoveOriginal);
	int portionX = -portion.x;
	int portionY = -portion.y;
	// fin parche
	int xMoved = portionX + xMove;
	int yMoved = portionY + yMove;
	int yMax = this->guiMap->getTotalHeight() - portion.h;
	int xMax = this->guiMap->getTotalWidth() - portion.w;
	// al hacer las restas de arriba, alguno de los valores puede quedar negativo, para evitar eso, les asigno 0 en esos casos
	if (xMax < 0) { xMax = 0;}
	if (yMax < 0) { yMax = 0;}

	// si el viewport aun no llego al limite, pero sumandole el scrollSpeed se pasaria del limite,
	// se adapta el valor de xMove o yMove para que se desplace hasta el limite del mapa, y no mas alla.
	if (xMoved > xMax && portionX <= xMax){
		xMove = xMax - portionX;
		*xMoveOrigial = -xMove;
	}
	if (xMoved < 0 && portionX >= 0){
		xMove = -portionX;
		*xMoveOrigial = -xMove;
	}
	if (yMoved > yMax && portionY <= yMax){
		yMove = yMax - portionY;
		*yMoveOriginal = -yMove;
	}
	if (yMoved < 0 && portionY >= 0){
		yMove = -portionY;
		*yMoveOriginal = -yMove;
	}
}

// Este metodo fue reemplazado por adaptScrollToLimits, posiblemente ya no sea necesario.
bool Viewport::checkForLimits(Portion portion, int xMove, int yMove) {
	// inicio parche (se invierten los valores)
	xMove = -xMove;
	yMove = -yMove;
	int portionX = -portion.x;
	int portionY = -portion.y;
	// fin parche
	int xMoved = portionX + xMove;
	int yMoved = portionY + yMove;
	int yMax = this->guiMap->getTotalHeight() - portion.h;
	int xMax = this->guiMap->getTotalWidth() - portion.w;
	// al hacer las restas de arriba, alguno de los valores puede quedar negativo, para evitar eso, les asigno 0 en esos casos
	if (xMax < 0) { xMax = 0;}
	if (yMax < 0) { yMax = 0;}
	if (xMoved < 0 || xMoved > xMax || yMoved < 0 || yMoved > yMax) {
		return false;
	}
	return true;
}

util::LinkedList<GUIObject> Viewport::getObjectsToDraw() {
	Portion portion;
	portion.x = 0;
	portion.y = 0;
	portion.h = this->guiMap->getTotalHeight();
	portion.w = this->guiMap->getTotalWidth();
	util::LinkedList<GUIObject> mapObjects = this->guiMap->getGUIObjects();
	util::LinkedList<GUIObject>::Iterator routeObjectsIterator = this->guiRoute->getGUIObjects().iterator();
	while (routeObjectsIterator.hasNext()) {
		mapObjects.append(routeObjectsIterator.next());
	}
	return mapObjects;

}

Portion Viewport::getPortionToDraw() {
	return this->portionToDraw;
}

Portion Viewport::getPortionLocation() {
	return this->portionLocation;
}

GUIMap* Viewport::getGUIMap() {
	return this->guiMap;
}

Surface* Viewport::getPlainSurface(){
	Surface* plainSurface;
	Surface* aux;
	aux = this->background->getPortion(this->getPortionToDraw());
	plainSurface->applySurface(aux);
	delete aux;

	this->middleLayers;

	aux = this->foreground->getPortion(this->getPortionToDraw());
	plainSurface->applySurface(aux);
	delete aux;

	return NULL;
}

}
